/**
 * Progetto PRAISED.
 * 27/gen/2011
 */
package it.uniroma3.dia.praised.core.dictionary;


import it.uniroma3.dia.praised.core.exception.DictionaryException;
import it.uniroma3.dia.praised.core.model.CandidateProteinName;
import it.uniroma3.dia.praised.core.model.InputText;
import it.uniroma3.dia.praised.core.model.ProteinName;
import it.uniroma3.dia.praised.core.model.Rank;
import it.uniroma3.dia.praised.core.util.Utils;

import java.io.IOException;
import java.util.concurrent.Callable;

import com.aliasi.spell.EditDistance;
import com.aliasi.spell.JaccardDistance;
import com.aliasi.spell.WeightedEditDistance;
import com.aliasi.tokenizer.IndoEuropeanTokenizerFactory;
import com.aliasi.tokenizer.TokenizerFactory;

/**
 * @author Accosta Augusto
 * @author Lopez Elisabetta
 *
 */
public class CalculateRankWorker implements Callable<CandidateProteinName> {

	private InputText inputText;
	private ProteinName proteinName;
	private Rank rank;
	
	/**
	 * @param rank 
	 * @param proteinName 
	 * @param inputText 
	 * 
	 */
	public CalculateRankWorker(InputText inputText, ProteinName proteinName, Rank rank) {
		this.inputText = inputText;
		this.proteinName = proteinName;
		this.rank = rank;
	}

	@Override
	public CandidateProteinName call() throws Exception {
		
		this.rank.setEditRank(editDistance(inputText, proteinName));
		this.rank.setJaccardRank(jaccardDistance(inputText, proteinName));
		this.rank.setWeightedRank(weightedEditDistance(inputText, proteinName));
		
		this.rank.generateTotalRankThroughtWeightedFunction();
		
		if(checkRank(this.rank))
			return new CandidateProteinName(proteinName, rank);
		
		return null;
	}
	
	/**
	 * 
	 * @param inputText
	 * @param proteinName
	 * @return
	 */
	private double editDistance(InputText inputText, ProteinName proteinName) {
		EditDistance distance = new EditDistance(true);
		return distance.distance(inputText.getParsedTextInput(), proteinName.getFullName());
	}
	
	/**
	 * 
	 * @param inputText
	 * @param proteinName
	 * @return
	 */
	private double weightedEditDistance(InputText inputText, ProteinName proteinName) {
		
		WeightedEditDistance distance = new WeightedEditDistance() {
			
			@Override
			public double transposeWeight(char arg0, char arg1) {
				return Double.NEGATIVE_INFINITY;
			}
			
			@Override
			public double substituteWeight(char arg0, char arg1) {
				return ((Character.toString(arg0).equals("-") && Character.toString(arg1).equals(" ")) ||
						(Character.isDigit(arg0) && Character.isDigit(arg1)) ||
						(Character.isLowerCase(arg0) && Character.isUpperCase(arg1)))
						? -10d
						: -50d;
			}
			
			@Override
			public double matchWeight(char arg0) {
				return 0;
			}
			
			@Override
			public double insertWeight(char arg0) {
				return (Character.toString(arg0).equals(" ") || Character.toString(arg0).equals("-"))
						? -10d 
						: -100d;
			}
			
			@Override
			public double deleteWeight(char arg0) {
				return (Character.toString(arg0).equals(" ") || Character.toString(arg0).equals("-"))
						? -10d 
						: -100d;
			}
		};
		
		return distance.distance(inputText.getParsedTextInput(), proteinName.getFullName());
		
	}
	
	/**
	 * 
	 * @param inputText
	 * @param proteinName
	 * @return
	 */
	private double jaccardDistance(InputText inputText, ProteinName proteinName) {
		 TokenizerFactory tokenizerFactory = IndoEuropeanTokenizerFactory.INSTANCE;
	     JaccardDistance jaccard = new JaccardDistance(tokenizerFactory);
	     
	     return jaccard.distance(inputText.getParsedTextInput(), proteinName.getFullName()); 
	}
	
	/**
	 * 
	 * @param rank
	 * @return
	 * @throws DictionaryException
	 */
	private boolean checkRank(Rank rank) throws DictionaryException {
		try {
			double threshold_jaccard = Double.parseDouble(Utils.loadProperty("Config.properties", "threshold_jaccard"));
			double threshold_edit = Double.parseDouble(Utils.loadProperty("Config.properties", "threshold_edit"));
			double threshold_weighted = Double.parseDouble(Utils.loadProperty("Config.properties", "threshold_weighted"));
			
			if( (rank.getJaccardRank() <= threshold_jaccard) ||
				((rank.getEditRank() <= threshold_edit) &&
				 (rank.getWeightedRank() <= threshold_weighted)) )
				return true;
			
		} catch (NumberFormatException e) {
			throw new DictionaryException(e);
		} catch (IOException e) {
			throw new DictionaryException(e);
		}
		return false;
	}
}
